#ifndef __UTIL__CONSTRAINED_VALUE_HPP__
#define __UTIL__CONSTRAINED_VALUE_HPP__

namespace util {
  /* @brief A class to constrain a POD type to a range.

     Based on "Constrained Value Types Using Polices", Christopher
     Diggins, C/C++ User's Journal, December 2004.

     This implementation includes some additional features and
     policies not found Diggin's implementation.
  */
  template <typename constraints_policy>
  class constrained_value {
  public:

    typedef constrained_value<constraints_policy> self;
    typedef typename constraints_policy::value    value;

    /** @brief Default constructor */
    constrained_value() : m() {}

    /** @brief Copy constructor */
    constrained_value(const self& x) {
      constraints_policy::assign(x.get_value(), m);
    } 

    /** @brief Constructor from value */
    constrained_value(const value& x) {
      constraints_policy::assign(x, m);
    }

    /** @brief Return the value */
    const value get_value() const {
      return m;
    }

    /** @brief Cast to the value type */
    operator value() {
      return m;
    }

    /** @brief Assignment */
    self& operator=(const self& x) {
      constraints_policy::assign(x.get_value(), m);
      return *this;
    }

    /** @brief Assignment */
    self& operator=(const value& x) {
      constraints_policy::assign(x, m);
      return *this;
    }

    /** @brief Additive assignment */
    self& operator+=(const self& x) {
      constraints_policy::assign(m + x.get_value(), m);
      return *this;
    }

    /** @brief Additive assignment */
    self& operator+=(const value& x) {
      constraints_policy::assign(m + x, m); return *this;
    }

    /** @brief Subtractive assignment */
    self& operator-=(const self& x) {
      constraints_policy::assign(m - x.get_value(), m); return *this;
    }

    /** @brief Subtractive assignment */
    self& operator-=(const value& x) {
      constraints_policy::assign(m - x, m); return *this;
    }

    /** @brief Multiplicative assignment */
    self& operator*=(const self& x) {
      constraints_policy::assign(m * x.get_value(), m); return *this;
    }

    /** @brief Multiplicative assignment */
    self& operator*=(const value& x) {
      constraints_policy::assign(m * x, m); return *this;
    }

    /** @brief Division assignment */
    self& operator/=(const self& x) {
      constraints_policy::assign(m / x.get_value(), m); return *this;
    }

    /** @brief Division assignment */
    self& operator/=(const value& x) {
      constraints_policy::assign(m / x, m); return *this;
    }

  private:
    value m;     
  };          

  namespace cv_policies {
    /** @brief This policy will throw an exception of 0 when the value
	is out of range */
    struct throwing {
      template <typename T> 
      static void on_below(const T& rvalue, T& lvalue, const T& min, const T& max) {
	throw 0;
      }

      template <typename T>
      static void on_above(const T& rvalue, T& lvalue, const T& min, const T& max) {
	throw 0;
      }
    };

    /** @brief This policy will set the value to the min or max when
	it is out of range */
    struct saturating {
      template <typename T> 
      static void on_below(const T& rvalue, T& lvalue, const T& min, const T& max) {
	lvalue = min;
      }

      template <typename T> 
      static void on_above(const T& rvalue, T& lvalue, const T& min, const T& max) {
	lvalue = max;
      }
    };

    /** @brief This policy will cycle through the periodic range of
	[min, max] when the assignment is out of range */
    struct periodic {
      template <typename T> 
      static void on_below(const T& rvalue, T& lvalue, const T& min, const T& max) {
	const int range = max - min + 1;
	lvalue = rvalue;
	while (lvalue < min) lvalue += range;
      }

      template <typename T> 
      static void on_above(const T& rvalue, T& lvalue, const T& min, const T& max) {
	const int range = max - min + 1;
	lvalue = rvalue;
	while (lvalue > max) lvalue -= range;
      }
    };

    /** @brief A policy for an int bounded to a range
	@param min The minimal value
	@param max The max value
	@param invalid_range The policy for the out of range behaviour
    */
    template <int min, int max, typename invalid_range = saturating>
    struct ranged_int {
      typedef int value;
      static const int get_min() {
	return min;
      }

      static const int get_max() {
	return max;
      }

      static void assign(const value& rvalue, value& lvalue) {  
        if (rvalue < min)
	  invalid_range::on_below(rvalue, lvalue, min, max);
        else if (rvalue > max)
	  invalid_range::on_above(rvalue, lvalue, min, max);
        else
	  lvalue = rvalue; 
      }
    };

    /** @brief A policy for a double bounded to the closed range of
	       zero to one
	@param invalid_range The policy for the out of range behaviour
    */
    template <typename invalid_range = saturating>
    struct double_0_to_1 {
      typedef double value;
      static void assign(const value& rvalue, value& lvalue) {
        if (rvalue < 0.0)
	  invalid_range::on_below(rvalue, lvalue, 0.0, 1.0);
        else if (rvalue > 1.0)
	  invalid_range::on_above(rvalue, lvalue, 0.0, 1.0);
        else
	  lvalue = rvalue; 
      }
    };
  }

  typedef constrained_value<cv_policies::double_0_to_1<> > d01;
}

#endif
